package org.duang.http.core;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.Future;

import org.apache.http.HttpResponse;
import org.duang.config.Config;
import org.duang.http.common.ClientException;
import org.duang.http.common.HttpMessage;
import org.duang.http.common.RequestMessage;
import org.duang.http.common.ResponseMessage;
import org.duang.http.core.enums.HttpMethod;
import org.duang.http.util.HttpUtil;
import org.duang.kit.HttpKit;
import org.duang.kit.ToolsKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class ServiceClient {

	public static class Request extends HttpMessage {

		private String url;
		private HttpMethod method;

		public String getUrl() {
			return url;
		}

		public void setUrl(String url) {
			this.url = url;
		}

		public HttpMethod getMethod() {
			return method;
		}

		public void setMethod(HttpMethod method) {
			this.method = method;
		}

		public Request() {
			super();
		}

		public Request(String uri, HttpMethod method) {
			super();
			this.url = uri;
			this.method = method;
		}
	}

	private final static int DEFAULT_MARK_LIMIT = 1024 * 4;
	private static final Logger logger = LoggerFactory.getLogger(ServiceClient.class);

	protected ClientConfig config;

	public ServiceClient() {
		config = new ClientConfig();
	}
	
	public <T> Future<HttpResponse> sendRequest(RequestMessage requestMessage, HttpCallBack<T> callback) throws ClientException {
		int retries = 0;
		ResponseMessage response = null;
		InputStream content = requestMessage.getContent();
		if (ToolsKit.isNotEmpty(content) && content.markSupported()) {
			content.mark(DEFAULT_MARK_LIMIT);
		}
		Future<HttpResponse> future = null;
		do {
			Request request = null;
			try {
				request = builderRequest(requestMessage);
				future = sendRequestImpl(request, callback);
				break;
			} catch (Exception e) {
				retries++;
				pause(retries, e);
			} finally {
				close(request, response);
			}

		} while (retries < config.getMaxErrorRetry());
		return future;
	}

	protected abstract <T> Future<HttpResponse> sendRequestImpl(Request request, HttpCallBack<T> callback) throws Exception;

	/**
	 * 构建请求对象
	 * 
	 * @param requestMessage
	 * @return
	 * @throws ClientException
	 */
	private Request builderRequest(RequestMessage requestMessage) throws ClientException {
		Request request = new Request();		
		request.setMethod(requestMessage.getMethod());
		request.setHeaders(requestMessage.getHeaders());
		if (null != requestMessage.getHeaders()) {
			HttpUtil.converHeaderCharsetToIso88591(request.getHeaders());
		}
		// 确定请求URL
		final String delimiter = "/";
		String url = requestMessage.getUrl().toString();
		boolean isPost = request.getMethod() == HttpMethod.POST;
		String paramString = "";
		try {
			paramString = HttpUtil.paramToQueryString(requestMessage.getParams());
		} catch (UnsupportedEncodingException e) {
			logger.warn(e.getMessage(), e);
			throw new ClientException("EncodeingFailed: " + e.getMessage());
		}
		if (!isPost && ToolsKit.isEmpty(requestMessage.getContent()) && ToolsKit.isNotEmpty(paramString)) {
			url += "?" + paramString;
		}
		if (isPost && ToolsKit.isEmpty(requestMessage.getContent()) && ToolsKit.isNotEmpty(paramString)) {			
			try {
				byte[] bytes = paramString.getBytes(config.getEncode());
				InputStream content = new ByteArrayInputStream(bytes);
				request.setContent(content);
				request.setContentLength(bytes.length);
			} catch (UnsupportedEncodingException e) {
				logger.warn(e.getMessage(), e);
				throw new ClientException("EncodeingFailed: " + e.getMessage());
			}
		} else {
			request.setContent(requestMessage.getContent());
			request.setContentLength(requestMessage.getContentLength());
		}
//		logger.debug(ToolsKit.inputStream2String(request.getContent()));
		request.setUrl(url);
		return request;
	}

	private void pause(int retries, Exception e) {
		logger.warn(e.getMessage(), e);
		if (retries < config.getMaxErrorRetry()) {
			long millis = retries * 1000;
			try {
				Thread.sleep(millis);
			} catch (InterruptedException ie) {
				ie.printStackTrace();
				logger.warn(e.getMessage(), ie);
			}
		} else {
			throw new ClientException(
					"request retries than " + config.getMaxErrorRetry() + ", request fail and exit...");
		}
	}

	private void close(Request request, ResponseMessage response) {
		try {
			if(null != request) request.close();
			if(null != response) response.close();
		} catch (IOException e) {
			logger.warn(e.getMessage(), e);
		}
	}

}
